﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ReversiBackend;
using System.Drawing.Drawing2D;
using System.Reflection;
using System.IO;
using System.Resources;
using ReversiFrontend.Properties;
using System.Diagnostics;

namespace ReversiFrontend
{
    public enum StoneLayouts { HelloKitty = 1, Default = 0, Marble = 2, Monster = 3 };
    public partial class CellControl : Control
    {
        public Cell Cell { get; private set; }
        private ResourceManager rm;
        public CellControl(Cell cell)
        {
            Cell = cell;
            InitializeComponent();
            DoubleBuffered = true;
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            BackgroundImageLayout = ImageLayout.Zoom;
            AnimatedValueChanged += new AnimatedValueChangedHandler(CellControl_AnimatedValueChanged);
            StoneLayoutChanged += new StoneLayoutChangedHandler(CellControl_StoneLayoutChanged);
            
            rm = new ResourceManager("ReversiFrontend.Properties.Resources", typeof(Resources).Assembly);
                    
        }

        public CellControl() : this(new Cell()) { }

        #region Events: AnimatedValueChanged und StoneLayoutChanged

        public delegate void AnimatedValueChangedHandler();
        public static event AnimatedValueChangedHandler AnimatedValueChanged;
        private static bool animated = true;
        public static bool Animated
        {
            get
            {
                return animated;
            }
            set
            {
                if (animated != value)
                {
                    animated = value;
                    if (AnimatedValueChanged != null)
                    {
                        AnimatedValueChanged();
                    }
                }
            }
        }
        void CellControl_AnimatedValueChanged()
        {
            Invalidate();
        }

        //StoneLayoutChanged
        public delegate void StoneLayoutChangedHandler();
        public static event StoneLayoutChangedHandler StoneLayoutChanged;
        private static string stoneLayout = "anim00{0:00}";
        public static StoneLayouts StoneLayout
        {
            get
            {
                switch (stoneLayout)
                {
                    case "hkanim00{0:00}":

                        return StoneLayouts.HelloKitty;
                    case "marbleanim00{0:00}":
                        return StoneLayouts.Marble;
                    case "anim00{0:00}":
                        return StoneLayouts.Default;
                    case "monsteranim00{0:00}":
                        return StoneLayouts.Monster;
                     default:
                        throw new Exception("unbekanntes stone layout");
                }
            }
            set
            {
                if (StoneLayout != value)
                {
                    switch (value)
                    {
                        case StoneLayouts.HelloKitty:

                            stoneLayout = "hkanim00{0:00}";
                            break;
                        case StoneLayouts.Marble:
                            stoneLayout = "marbleanim00{0:00}";
                            break;
                        case StoneLayouts.Default:
                            stoneLayout = "anim00{0:00}";
                            break;
                        case StoneLayouts.Monster:
                            stoneLayout = "monsteranim00{0:00}";
                            break;
                    } if (StoneLayoutChanged != null)
                    {
                        StoneLayoutChanged();
                    }

                }
            }
        }
        void CellControl_StoneLayoutChanged()
        {
            string name = String.Format(stoneLayout, imgIdx);
            BackgroundImage = (Image)rm.GetObject(name);
        }

        #endregion

        #region Stone Animation Stuff: imgIndex, AnimImg
        private int imgIdx_ = 0;
        private int imgIdx
        {
            get { return imgIdx_; }
            set
            {
                if (value != imgIdx_)
                {
                    if (0 < value && value < 21)
                    {
                        string name = String.Format(stoneLayout, value);
                        BackgroundImage = (Image)rm.GetObject(name);
                    }
                    else
                    {
                        BackgroundImage = null;
                    }
                    imgIdx_ = value;
                }
            }
        }
        private Timer animTimer;
        private bool animating = false;
        private void AnimImg(int start, int stop)
        {
            animTimer = new Timer();
            imgIdx = start;
            Invalidate();
            animTimer.Interval = 40;
            animating = true;

            animTimer.Tick += (s, a) =>
            {
                if (animating)
                {
                    if (++imgIdx >= stop)
                    {
                        animTimer.Stop();
                        animating = false;
                        animTimer.Dispose();
                    }
                    else
                    {
                        // image is auto updated from imgIdx
                    }
                }
                else
                {
                    if (animTimer != null)
                    {
                        animTimer.Stop();
                    }
                }
                //Invalidate();
            };
            animTimer.Enabled = true;
        }
        #endregion


        private int value_;
        /// <summary>
        /// Wert des Feldes. Aktualisiert automatisch bei Änderung die Anzeige
        /// </summary>
        /// <value>
        /// -1 = Chip von Spieler1, 1 = Chip von Spieler2, 0 = kein Chip
        /// </value>
        public int Value
        {
            get
            {
                return value_;
            }
            set
            {
                if (value != value_)
                {
                    if (Math.Abs(value) > 1)
                    {
                        throw new Exception("Invalid Value: " + value);
                    }

                    if (animated)
                    {
                        if (value != 0)
                        {
                            if (value_ != 0)
                            {
                                // wenn neuer wert -1 = schwarz ist, starte bei weißem stein und
                                // drehe dann zu schwarz, sonst starte bei schwarz
                                imgIdx = value == -1 ? 11 : 1;
                                AnimImg(imgIdx, imgIdx + 9);
                            }
                            else
                            {
                                // do not animate, if a new stone/chip is put on an empty cell
                                imgIdx = value == -1 ? 1 : 11;
                                animating = false;
                            }
                        }
                        else
                        {
                            if (animTimer != null)
                            {
                                animTimer.Stop();
                                animTimer = null;
                            }
                            imgIdx = -1;
                        }
                    }
                    else
                    {
                        imgIdx = -1;
                    }
                    value_ = value;
                    Invalidate();
                }
            }
        }

        #region Anzeige-optionen-Properties: isValidField, isClickedInvalifField isLastPlayerMove
        private bool? isValidField;
        /// <summary>
        /// Zeigt an, ob dieses Feld einen erlaubten Zug darstellt.
        /// Auf "null" setzen, um Anzeige zu unterbinden.
        /// </summary>
        /// <value>
        /// true/false = erlaubes/nicht erlaubtes Feld, null = keine information/anzeige
        /// </value>
        /// 
        public bool? IsValidField
        {
            get
            {
                return isValidField;
            }
            set
            {
                if (value != isValidField)
                {
                    isValidField = value;
                    Refresh();
                }
            }
        }
        private Timer t;
        private int fadeIndexer;
        private bool? isClickedInvalidField;
        /// <summary>
        /// Markiert ein angeklicktes Feld Rot, wenn der Spielzug ungültig war.
        /// </summary>
        /// <value>
        /// true/false = unerlaubt geclicktes Feld/normale Anzeige, null = keine information/anzeige
        /// </value>
        /// 
        public bool? IsClickedInvalidField
        {
            get
            {
                return isClickedInvalidField;
            }
            set
            {
                if (value != isClickedInvalidField)
                {
                    isClickedInvalidField = value;
                    if (value == true)
                    {
                        t = new Timer();
                        t.Interval = 20;
                        fadeIndexer = 255;
                        t.Tick += new EventHandler((sender, args) =>
                        {
                            Refresh();
                        });
                        t.Start();
                    }
                    else
                    {
                        t.Stop();
                        t.Dispose();
                    }
                }
            }
        }
        private bool? isLastPlayerMove;
        /// <summary>
        /// Markiert das letzte Feld auf das ein Stein gesetzt wurde
        /// </summary>
        /// <value>
        /// true/false = als letztes bespieltes/in folge des zuges umgedrehtes Feld, null = keine information/anzeige
        /// </value>
        /// 
        public bool? IsLastPlayerMove
        {
            get
            {
                return isLastPlayerMove;
            }
            set
            {
                if (value != isLastPlayerMove)
                {
                    isLastPlayerMove = value;
                    if (value == true)
                    {
                        
                        t = new Timer();
                        t.Interval = 20;
                        fadeIndexer = 200;
                        t.Tick += new EventHandler((sender, args) =>
                        {
                            Refresh();
                        });
                        t.Start();
                    }
                    else
                    {
                        t.Stop();
                        t.Dispose();
                    }
                }
            }
        }
        #endregion

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
            pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            if (isLastPlayerMove == true)
            {
                    if ((fadeIndexer -= 4) >= 0)
                    {
                        Brush br = new SolidBrush(Color.FromArgb(fadeIndexer, Color.White));
                        Rectangle re = new Rectangle(0, 0, Width, Height);
                        pe.Graphics.FillRectangle(br, re);
                    }
                    else
                    {
                        IsLastPlayerMove = false;
                    }
            }
            //wird nur erreicht, wenn das Feld ein angeklicktes, aber ungültiges Feld ist
            if (isClickedInvalidField == true)
            {
                if ((fadeIndexer -= 5) >= 0)
                {
                    Pen errPen = new Pen(Color.FromArgb(fadeIndexer, Color.Red), 10);
                    int x1 = 5, x2 = Width - 5;
                    int y1 = 5, y2 = Height - 5;
                    //Rectangle re = new Rectangle(0, 0, Width, Height);
                    //pe.Graphics.FillRectangle(br, re);
                    pe.Graphics.DrawLine(errPen, new Point(x1, y1), new Point(x2, y2));
                    pe.Graphics.DrawLine(errPen, new Point(x1, y2), new Point(x2, y1));
                }
                else
                {
                    isClickedInvalidField = false;
                }
            }
            // sollen keine hinweise gezeigt werden, ist "isValidVield" null, wenns auf false ist,
            // soll dies auch nicht gezeigt werden
            if (isValidField == true)
            {
                pe.Graphics.FillRectangle(new LinearGradientBrush(new Point(0, 0), new Point (Width, Height),
                                Color.FromArgb(255,ControlPaint.Dark(BoardControl.BaseColorGetter)),
                                Color.FromArgb(0, ControlPaint.Dark(BoardControl.BaseColorGetter))),
                                new Rectangle(0,0,Width, Height));
            }


            if (!animated)
            {
                if (Value != 0)
                {
                    GraphicsPath gp = new GraphicsPath();
                    gp.AddEllipse(new Rectangle(0, 2, Width - 2, Height - 2));

                    PathGradientBrush pgb = new PathGradientBrush(gp);

                    pgb.CenterPoint = new PointF(Width / 2 - 5,
                                                 Height / 2 + 5);
                    pgb.CenterColor = Value == -1 ? Color.FromArgb(50, 50, 50) : Color.Gray;
                    pgb.SurroundColors = new Color[] { Value == -1 ? Color.FromArgb(150, 150, 150) : Color.Wheat };
                    //pgb.SetBlendTriangularShape(.5f, 1.0f);
                    pgb.FocusScales = new PointF(0, 0f);

                    pe.Graphics.FillEllipse(new SolidBrush(Value == -1 ? Color.FromArgb(50, 50, 50) : Color.Gray),
                                            new Rectangle(2, 0, Width - 2, Height - 2));
                    pe.Graphics.FillPath(pgb, gp);


                    pgb.Dispose();
                    gp.Dispose();
                    //pe.Graphics.FillEllipse(Value == -1 ? Brushes.Wheat : new SolidBrush(Color.FromArgb(50, 50, 50)),
                    //                      new Rectangle(new Point(0, 0), Size));
                }
            }
            else
            {
                if (!animating && value_ != 0)
                {
                    imgIdx = (value_ == -1) ? 1 : 11;

                }
            }
        }
    }
}
